#include "myApplication.h"

myApplication::myApplication()
	: m_fps(30)
	, m_worldSizeY(100)
	, m_worldSizeX(m_worldSizeY)
{
}
myApplication::~myApplication()
{
}
bool myApplication::LoadTGA(TextureImage *texture, char *filename){	// Loads A TGA File Into Memory
	GLubyte		TGAheader[12] = { 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 };	// Uncompressed TGA Header
	GLubyte		TGAcompare[12];								// Used To Compare TGA Header
	GLubyte		header[6];									// First 6 Useful Bytes From The Header
	GLuint		bytesPerPixel;								// Holds Number Of Bytes Per Pixel Used In The TGA File
	GLuint		imageSize;									// Used To Store The Image Size When Setting Aside Ram
	GLuint		temp;										// Temporary Variable
	GLuint		type = GL_RGBA;								// Set The Default GL Mode To RBGA (32 BPP)

	FILE *file = fopen(filename, "rb");						// Open The TGA File

	if (file == NULL ||										// Does File Even Exist?
		fread(TGAcompare, 1, sizeof(TGAcompare), file) != sizeof(TGAcompare) ||	// Are There 12 Bytes To Read?
		memcmp(TGAheader, TGAcompare, sizeof(TGAheader)) != 0 ||	// Does The Header Match What We Want?
		fread(header, 1, sizeof(header), file) != sizeof(header))				// If So Read Next 6 Header Bytes
	{
		if (file == NULL)									// Did The File Even Exist? *Added Jim Strong*
			return false;									// Return False
		else
		{
			fclose(file);									// If Anything Failed, Close The File
			return false;									// Return False
		}
	}

	texture->width = header[1] * 256 + header[0];			// Determine The TGA Width	(highbyte*256+lowbyte)
	texture->height = header[3] * 256 + header[2];			// Determine The TGA Height	(highbyte*256+lowbyte)

	if (texture->width <= 0 ||								// Is The Width Less Than Or Equal To Zero
		texture->height <= 0 ||								// Is The Height Less Than Or Equal To Zero
		(header[4] != 24 && header[4] != 32))					// Is The TGA 24 or 32 Bit?
	{
		fclose(file);										// If Anything Failed, Close The File
		return false;										// Return False
	}

	texture->bpp = header[4];							// Grab The TGA's Bits Per Pixel (24 or 32)
	bytesPerPixel = texture->bpp / 8;						// Divide By 8 To Get The Bytes Per Pixel
	imageSize = texture->width*texture->height*bytesPerPixel;	// Calculate The Memory Required For The TGA Data

	texture->imageData = (GLubyte *)malloc(imageSize);		// Reserve Memory To Hold The TGA Data

	if (texture->imageData == NULL ||							// Does The Storage Memory Exist?
		fread(texture->imageData, 1, imageSize, file) != imageSize)	// Does The Image Size Match The Memory Reserved?
	{
		if (texture->imageData != NULL)						// Was Image Data Loaded
			free(texture->imageData);						// If So, Release The Image Data

		fclose(file);										// Close The File
		return false;										// Return False
	}

	for (GLuint i = 0; i < int(imageSize); i += bytesPerPixel)		// Loop Through The Image Data
	{														// Swaps The 1st And 3rd Bytes ('R'ed and 'B'lue)
		temp = texture->imageData[i];							// Temporarily Store The Value At Image Data 'i'
		texture->imageData[i] = texture->imageData[i + 2];	// Set The 1st Byte To The Value Of The 3rd Byte
		texture->imageData[i + 2] = temp;					// Set The 3rd Byte To The Value In 'temp' (1st Byte Value)
	}

	fclose(file);											// Close The File

	// Build A Texture From The Data
	glGenTextures(1, &texture[0].texID);					// Generate OpenGL texture IDs

	glBindTexture(GL_TEXTURE_2D, texture[0].texID);			// Bind Our Texture
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);	// Linear Filtered
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);	// Linear Filtered

	if (texture[0].bpp == 24)									// Was The TGA 24 Bits
	{
		type = GL_RGB;										// If So Set The 'type' To GL_RGB
	}

	glTexImage2D(GL_TEXTURE_2D, 0, type, texture[0].width, texture[0].height, 0, type, GL_UNSIGNED_BYTE, texture[0].imageData);

	return true;											// Texture Building Went Ok, Return True
}

bool myApplication::Init()
{
	Math::InitRNG();
	m_fps = 0;

	glutFullScreen();
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	if (!LoadTGA(&HunterTexture[0], "hunter.tga"))
		return false;
	if (!LoadTGA(&MonsterTexture[0], "monster.tga"))
		return false;
	if (!LoadTGA(&Background[0], "background.tga"))
		return false;

	hunter = new CHunter(50, 50, 0, 100, 0, 25, 0);
	hunter->setCurrentState(State::STATE_IDLE);

	CMonster *monster = new CMonster(hunter);
	monster->Init();
	monsters.push_back(monster);
	for (int i = 0; i < 2; ++i){
		monster = new CMonster(hunter);
		monsters.push_back(monster);
	}
}

void myApplication::Exit()
{
}

void myApplication::SetupRenderContext()
{
	glClearColor(0.0, 0.0, 0.0, 0.0);	// set background colour to black
	glEnable(GL_CULL_FACE);
	glEnable(GL_DEPTH_TEST);
	glShadeModel(GL_SMOOTH);
	glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}

void myApplication::GlutReshape(GLsizei w, GLsizei h)
{
	if (h == 0)
		h = 1;

	float ratio = (float)w / h;
	m_worldSizeX = m_worldSizeY;	//Prev: m_worldSizeY * ratio

	glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(0, m_worldSizeX, 0, m_worldSizeY);
	glMatrixMode(GL_MODELVIEW);
}

void myApplication::GlutMouseClick(int button, int state, int x, int y)
{
}

void myApplication::GlutMouseMove(int x, int y)
{
}

void myApplication::GlutKeyboard(unsigned char key, int x, int y)
{
	switch (key)
	{
	case '1':
		for (vector<CMonster*>::iterator itr = monsters.begin(); itr != monsters.end(); ++itr){
			if (!(*itr)->isActive()){
				(*itr)->Init();
				break;
			}
		}
		break;

	case '2':
		hunter->setHp(100);
		hunter->setPosX(30);
		hunter->setPosY(30);
		hunter->setCurrentState(State::STATE_IDLE);
		break;

	case 27:
		exit(0);
	}
}

void myApplication::GlutSpecialKey(int key, int x, int y)
{
	switch (key)
	{
	case GLUT_KEY_LEFT:
		break;
	case GLUT_KEY_UP:
		break;
	case GLUT_KEY_RIGHT:
		break;
	case GLUT_KEY_DOWN:
		break;
	}
}

void myApplication::renderTexts(void)
{
	glColor3f(1, 1, 1);
	char frames[64];
	sprintf_s(frames, "FPS: %.2f", m_fps);
	RenderStringOnScreen(5, 90, frames);

	glColor3f(0, 1, 1);
	char hunterHP[64];
	sprintf_s(hunterHP, "HP: %i", hunter->getHp());
	RenderStringOnScreen(hunter->getPosX() - 2, hunter->getPosY() + 7, hunterHP);

	switch (hunter->getCurrentState())
	{
	case State::STATE_IDLE:
		glColor3f(0, 1, 1);
		char idleState[64];
		sprintf_s(idleState, "Idle");
		RenderStringOnScreen(hunter->getPosX() - 2, hunter->getPosY() - 7, idleState);
		break;

	case State::STATE_HUNT:
		glColor3f(0, 1, 1);
		char huntState[64];
		sprintf_s(huntState, "Hunt");
		RenderStringOnScreen(hunter->getPosX() - 2, hunter->getPosY() - 7, huntState);
		break;

	case State::STATE_ATTACK:
		glColor3f(0, 1, 1);
		char attackState[64];
		sprintf_s(attackState, "Attack");
		RenderStringOnScreen(hunter->getPosX() - 2, hunter->getPosY() - 7, attackState);
		break;

	case State::STATE_FLEE:
		glColor3f(0, 1, 1);
		char fleeState[64];
		sprintf_s(fleeState, "Flee");
		RenderStringOnScreen(hunter->getPosX() - 2, hunter->getPosY() - 7, fleeState);
		break;

	case State::STATE_DIE:
		glColor3f(0, 1, 1);
		char dieState[64];
		sprintf_s(dieState, "Die");
		RenderStringOnScreen(hunter->getPosX() - 2, hunter->getPosY() - 7, dieState);
		break;
	}

	for (vector<CMonster*>::iterator itr = monsters.begin(); itr != monsters.end(); ++itr){
		if ((*itr)->isActive())
		{
			glColor3f(1, 0.5, 0);
			char monsterHP[64];
			sprintf_s(monsterHP, "HP: %i", (*itr)->getHP());
			RenderStringOnScreen((*itr)->getPosition().x - 2, (*itr)->getPosition().y + 7, monsterHP);

			switch ((*itr)->getState())
			{
			case MonsterState::IDLE:
				glColor3f(1, 0.5, 0);
				char monHunt[64];
				sprintf_s(monHunt, "Idle");
				RenderStringOnScreen((*itr)->getPosition().x - 2, (*itr)->getPosition().y - 7, monHunt);
				break;
			case MonsterState::ATTACK:
				glColor3f(1, 0.5, 0);
				char monAtt[64];
				sprintf_s(monAtt, "Attack");
				RenderStringOnScreen((*itr)->getPosition().x - 2, (*itr)->getPosition().y - 7, monAtt);
				break;
			case MonsterState::CHASE:
				glColor3f(1, 0.5, 0);
				char monChase[64];
				sprintf_s(monChase, "Chase");
				RenderStringOnScreen((*itr)->getPosition().x - 2, (*itr)->getPosition().y - 7, monChase);
				break;
			case MonsterState::RETURN:
				glColor3f(1, 0.5, 0);
				char monReturn[64];
				sprintf_s(monReturn, "Return");
				RenderStringOnScreen((*itr)->getPosition().x - 2, (*itr)->getPosition().y - 7, monReturn);
				break;
			case MonsterState::DIE:
				glColor3f(1, 0.5, 0);
				char monDie[64];
				sprintf_s(monDie, "Die");
				RenderStringOnScreen((*itr)->getPosition().x - 2, (*itr)->getPosition().y - 7, monDie);
				break;
			}
		}
	}
}

void myApplication::GlutIdle()
{
	static int frame = 0;
	static int lastTime = glutGet(GLUT_ELAPSED_TIME);
	++frame;
	int time = glutGet(GLUT_ELAPSED_TIME);
	float dt = (time - lastTime) / 1000.f;
	lastTime = time;

	static int lastFPSTime = glutGet(GLUT_ELAPSED_TIME);
	if (time - lastFPSTime > 1000){
		m_fps = frame * 1000.f / (time - lastFPSTime);
		lastFPSTime = time;
		frame = 0;
	}

	if (hunter->getPosX() < 10)
	{
		hunter->setPosX(10);
		for (vector<CMonster*>::iterator itr = monsters.begin(); itr != monsters.end(); ++itr){
			if ((*itr)->isActive())
				(*itr)->setCurrentState(MonsterState::RETURN);
		}
	}
	if (hunter->getPosY() < 10)
	{
		hunter->setPosY(10);
		for (vector<CMonster*>::iterator itr = monsters.begin(); itr != monsters.end(); ++itr){
			if ((*itr)->isActive())
				(*itr)->setCurrentState(MonsterState::RETURN);
		}
	}
	if (hunter->getPosX() > m_worldSizeX - 10)
	{
		hunter->setPosX(m_worldSizeX - 10);
		for (vector<CMonster*>::iterator itr = monsters.begin(); itr != monsters.end(); ++itr){
			if ((*itr)->isActive())
				(*itr)->setCurrentState(MonsterState::RETURN);
		}
	}
	if (hunter->getPosY() > m_worldSizeY - 10)
	{
		hunter->setPosY(m_worldSizeY - 10);
		for (vector<CMonster*>::iterator itr = monsters.begin(); itr != monsters.end(); ++itr){
			if ((*itr)->isActive())
				(*itr)->setCurrentState(MonsterState::RETURN);
		}
	}

	for (vector<CMonster*>::iterator itr = monsters.begin(); itr != monsters.end(); ++itr){
		if ((*itr)->isActive()){
			if ((*itr)->getPosition().x < 0)
			{
				hunter->setPosX(m_worldSizeX);
			}
			if ((*itr)->getPosition().y < 0)
			{
				hunter->setPosX(m_worldSizeY);
			}
			if ((*itr)->getPosition().x > m_worldSizeX)
			{
				hunter->setPosX(0);
			}
			if ((*itr)->getPosition().y > m_worldSizeY)
			{
				hunter->setPosY(0);
			}
		}
	}

	for (vector<CMonster*>::iterator itr = monsters.begin(); itr != monsters.end(); ++itr){
		hunter->Update((*itr), dt);	//Update hunter

		if ((*itr)->isActive()){
			(*itr)->Update();		//Update monster

			//If monster is dead, destroy it
			if ((*itr)->getState() == MonsterState::DIE){
				(*itr)->setActive(false);
				hunter->setCurrentState(STATE_IDLE);

				if (monsters.size() > 0)	//Check if monsters is empty after destroying monster
					continue;
				else						//Stop iterating if empty
					break;
			}
		}
	}
}

void myApplication::GlutDisplay()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	renderTexts();
	glColor4f(1, 1, 1, 1);

	glPushMatrix();
	glEnable(GL_TEXTURE_2D);
	glEnable(GL_BLEND);
	glTranslatef(hunter->getPos().x, hunter->getPos().y, hunter->getPos().z);
	glBindTexture(GL_TEXTURE_2D, HunterTexture[0].texID);
	glBegin(GL_QUADS);
	glTexCoord2f(0, 1); glVertex2f(-2.5, 2.5);
	glTexCoord2f(0, 0); glVertex2f(-2.5, -2.5);
	glTexCoord2f(1, 0); glVertex2f(2.5, -2.5);
	glTexCoord2f(1, 1); glVertex2f(2.5, 2.5);
	glEnd();
	glDisable(GL_BLEND);
	glDisable(GL_TEXTURE_2D);
	glPopMatrix();

	for (vector<CMonster*>::iterator itr = monsters.begin(); itr != monsters.end(); ++itr){
		if ((*itr)->isActive()){
			glPushMatrix();
			glEnable(GL_TEXTURE_2D);
			glEnable(GL_BLEND);
			glTranslatef((*itr)->getPosition().x, (*itr)->getPosition().y, 0);
			glBindTexture(GL_TEXTURE_2D, MonsterTexture[0].texID);
			glBegin(GL_QUADS);
			glTexCoord2f(0, 1); glVertex2f(-2.5, 2.5);
			glTexCoord2f(0, 0); glVertex2f(-2.5, -2.5);
			glTexCoord2f(1, 0); glVertex2f(2.5, -2.5);
			glTexCoord2f(1, 1); glVertex2f(2.5, 2.5);
			glEnd();
			glDisable(GL_BLEND);
			glDisable(GL_TEXTURE_2D);
			glPopMatrix();
		}
	}

	/*glColor3f(1.0, 1.0, 1.0);
	char temp[128];
	sprintf_s(temp, "m_worldSizeX: %.2f", m_worldSizeX);
	RenderStringOnScreen(5, 10, temp);
	sprintf_s(temp, "m_worldSizeY: %.2f", m_worldSizeY);
	RenderStringOnScreen(5, 5, temp);*/

	glPushMatrix();
	glEnable(GL_TEXTURE_2D);
	glEnable(GL_BLEND);
	glBindTexture(GL_TEXTURE_2D, Background[0].texID);
	glBegin(GL_QUADS);
	glTexCoord2f(0, 1); glVertex2f(0, m_worldSizeY);
	glTexCoord2f(0, 0); glVertex2f(0, 0);
	glTexCoord2f(1, 0); glVertex2f(m_worldSizeX, 0);
	glTexCoord2f(1, 1); glVertex2f(m_worldSizeX, m_worldSizeY);
	glEnd();
	glDisable(GL_BLEND);
	glDisable(GL_TEXTURE_2D);
	glPopMatrix();

	glutSwapBuffers();
	glutPostRedisplay();
}

void myApplication::RenderStringOnScreen(float x, float y, const char* quote)
{
	int length = strlen(quote);
	glRasterPos2f(x, y);

	for (int i = 0; i < length; i++)
	{
		glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, quote[i]);
	}
}